package com.lancoo.cvas.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lancoo.cvas.entity.*;
import com.lancoo.cvas.exception.BizException;
import com.lancoo.cvas.mapper.*;
import com.lancoo.cvas.pojo.common.Deleted;
import com.lancoo.cvas.pojo.common.IsEnable;
import com.lancoo.cvas.pojo.common.PageInfo;
import com.lancoo.cvas.pojo.common.TeachingClassType;
import com.lancoo.cvas.pojo.constant.GlobalConstant;
import com.lancoo.cvas.pojo.dto.CacheTeachingClassDto;
import com.lancoo.cvas.pojo.dto.ScheduleDto;
import com.lancoo.cvas.pojo.dto.TeachingClassStudentDto;
import com.lancoo.cvas.pojo.dto.TeachingClassTimeCode;
import com.lancoo.cvas.pojo.excel.DeselectStudentToExcel;
import com.lancoo.cvas.pojo.excel.ReleaseSelectionToExcel;
import com.lancoo.cvas.pojo.response.basicPlatform.BaseStudent;
import com.lancoo.cvas.pojo.vo.*;
import com.lancoo.cvas.service.*;
import com.lancoo.cvas.util.BatchInsert;
import com.lancoo.cvas.util.MyExcelUtils;
import com.lancoo.cvas.util.PagingUtil;
import com.lancoo.cvas.util.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.support.IsNewStrategy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.awt.SunHints;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@Service("studentService")
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements StudentService {

    @Resource
    private StudentMapper studentMapper;
    @Resource
    private RedisUtil redisUtil;

    @Resource
    private PlanTeachingStudentMapper planTeachingStudentMapper;

    @Resource
    private PlanTeachingMapper planTeachingMapper;
    @Resource
    private TaskInfoService taskInfoService;
    @Resource
    private TeacherService teacherService;
    @Resource
    private ScheduleMapper scheduleMapper;
    @Resource
    private TeachingClassStudentMapper teachingClassStudentMapper;

    @Resource
    private CourseMapper courseMapper;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private RedisScript<Long> selectCourseLua;
    @Resource
    private RedisScript<Long> cancelCourseLua;

    @Resource
    private PlanTeachingService planTeachingService;

    @Resource
    private TeachingClassStudentService teachingClassStudentService;

    @Resource
    private TeachingClassMapper teachingClassMapper;

    @Resource
    private StudentRetakeMapper studentRetakeMapper;


    @Resource
    private CssTaskInfoService cssTaskInfoService;

    @Resource
    private CssTaskRoundService cssTaskRoundService;
    @Resource
    private ClassInfoService classInfoService;
    @Resource
    private PlanTeachingCourseSegmentMapper planTeachingCourseSegmentMapper;
    @Resource
    private MajorService majorService;
    @Resource
    private ParameterSettingService parameterSettingService;
    @Resource
    private StudentRetakeService studentRetakeService;


    @Override
    public void syncBaseStudent(Integer taskId, List<BaseStudent> baseStudents) {
        List<Student> students = new ArrayList<>();
        for (BaseStudent baseStudent : baseStudents) {
            students.add(baseStudent.toStudent(taskId));
        }
        //批量插入教学班学生，大数据批量插入
        Consumer<List<Student>> studentConsumer = o -> studentMapper.addBatch(o);
        BatchInsert.batchSplitInsert(students, studentConsumer);
    }

    /**
     * 根据基础平台id获取学生信息
     *
     * @param taskId
     * @param baseStudentId
     * @return
     */
    @Override
    public Student getStudentByBaseStudentId(Integer taskId, String baseStudentId) {
        Student student = this.getOne(new LambdaQueryWrapper<Student>().eq(Student::getTaskId, taskId)
                .eq(Student::getBaseStudentId, baseStudentId).eq(Student::getDeleted, Deleted.NOT.getCode()));
        return student;
    }

    /**
     * 获取学生可选的教学班课程 已选教学班 选课要求
     *
     * @param getAvailableCondition
     * @return
     */
    @Override
    public StudentTeachingClassView getAvailableTeachingClass(GetAvailableCondition getAvailableCondition) {
        if (getAvailableCondition.getTeachingClassType().equals(1)) {
            return this.getAvailableTeachingClassOnline(getAvailableCondition);
        }

        return new StudentTeachingClassView();
    }

    @Override
    public int selectCourseOptimize(AddSelectCourse addSelectCourse) {
        try {
            //构建键值列表
            String teachingClassDetailKey = getCacheKeys(addSelectCourse.getTaskId(), null, 6, null);
            String studentOptionalClassKey = getCacheKeys(addSelectCourse.getTaskId(), addSelectCourse.getStudentId(), 4, addSelectCourse.getTeachingClassType() == 1 ? "Online" : "Primary");
            String teachingClassStudentKey = getCacheKeys(addSelectCourse.getTaskId(), null, 3, null);

            List<String> keys = ListUtil.toLinkedList(teachingClassDetailKey, studentOptionalClassKey, teachingClassStudentKey);
            Long execute = redisTemplate.execute(selectCourseLua, keys, addSelectCourse.getStudentId(), addSelectCourse.getTeachingClassId(),
                    LocalDateTime.now(), addSelectCourse.getCancelTeachingClassId(), addSelectCourse.getTeachingClassType());

            //查找选课教学班
            TeachingClass teachingClass = teachingClassMapper.selectOne(new LambdaQueryWrapper<TeachingClass>()
                    .eq(TeachingClass::getTaskId, addSelectCourse.getTaskId())
                    .eq(TeachingClass::getTeachingClassId, addSelectCourse.getTeachingClassId())
                    .eq(TeachingClass::getDeleted, Deleted.NOT.getCode()));

            if (ObjUtil.isNotNull(teachingClass)) {
                //查找选课课程
                Course course = courseMapper.selectOne(new LambdaQueryWrapper<Course>()
                        .eq(Course::getTaskId, addSelectCourse.getTaskId()).eq(Course::getCourseId, teachingClass.getCourseId())
                        .eq(Course::getDeleted, Deleted.NOT.getCode()));
                //查找是否为重修学生
                Student student = studentMapper.selectOne(new LambdaQueryWrapper<Student>().eq(Student::getTaskId, addSelectCourse.getTaskId())
                        .eq(Student::getStudentId, addSelectCourse.getStudentId()).eq(Student::getDeleted, Deleted.NOT.getCode()));
                if (ObjUtil.isNotNull(course) && ObjUtil.isNotNull(student)) {
                    //修改重修学生信息
                    StudentRetake studentRetake = studentRetakeMapper.selectOne(new LambdaQueryWrapper<StudentRetake>()
                            .eq(StudentRetake::getTaskId, addSelectCourse.getTaskId())
                            .eq(StudentRetake::getBaseStudentId, student.getBaseStudentId())
                            .eq(StudentRetake::getBaseCourseId, course.getBaseCourseId())
                            .eq(StudentRetake::getDeleted, Deleted.NOT.getCode()));

                    if (ObjUtil.isNotNull(studentRetake)) {
                        studentRetakeMapper.update(studentRetake, new LambdaUpdateWrapper<StudentRetake>().set(StudentRetake::getIsAllocation, 1));
                    }
                }
            }
            return execute.intValue();
        } catch (RedisConnectionFailureException e) {
            return -2;
        }
    }

    @Cacheable(cacheNames = "StudentTeachingClass:Normal", key = "#taskId+':'+#studentId")
    @Override
    public List<StudentTeachingClass> getNormalTeachingClass(Integer taskId, Long studentId) {
        List<StudentTeachingClass> normalTeachingClass = new ArrayList<>();
        //获取学生有那些教学班
        List<Long> classIds = teachingClassStudentMapper.selectList(new LambdaQueryWrapper<TeachingClassStudent>()
                .eq(TeachingClassStudent::getStudentId, studentId).eq(TeachingClassStudent::getFlag, 1)
                .eq(TeachingClassStudent::getDeleted, Deleted.NOT.getCode()))
                .stream().map(TeachingClassStudent::getTeachingClassId).distinct().collect(Collectors.toList());
        if (CollUtil.isNotEmpty(classIds)) {
            //课程教学班
            List<StudentTeachingClass> teachingClassList = studentMapper.getNormalCourseTeachingClass(classIds, 0);

            normalTeachingClass.addAll(teachingClassList);
        }

        List<Long> teachingClassIds = normalTeachingClass.stream().map(StudentTeachingClass::getTeachingClassId).distinct().collect(Collectors.toList());
        List<StudentTeachingClass> collect = normalTeachingClass.stream().distinct().collect(Collectors.toList());
        if (CollUtil.isNotEmpty(teachingClassIds)) {
            //填充教师信息
            Map<Long, List<Teacher>> teacherMap = teacherService.getTeacherByTeachingClassIds(teachingClassIds).stream()
                    .collect(Collectors.groupingBy(Teacher::getTeachingClassId));
            //课表信息
            List<ScheduleDto> scheduleDtoList = scheduleMapper.getAllScheduleByTeachingClassIds(null, teachingClassIds);
            Map<Long, List<ScheduleDto>> scheduleMap = scheduleDtoList.stream().collect(Collectors.groupingBy(ScheduleDto::getTeachingClassId));
            collect.forEach(a -> {
                List<Teacher> teacherList = teacherMap.get(a.getTeachingClassId());
                List<Teacher> teacherList1 = new ArrayList<>();
                Set<String> baseTeacherId = new HashSet<>();
                if (CollUtil.isNotEmpty(teacherList)) {
                    teacherList.forEach(o -> {
                        if (!baseTeacherId.contains(o.getBaseTeacherId())) {
                            baseTeacherId.add(o.getBaseTeacherId());
                            teacherList1.add(o);
                        }
                    });
                    a.setTeachers(teacherList1);
                }
                a.setSchedules(scheduleMap.get(a.getTeachingClassId()));
            });
        }
        return collect;
    }

    /**
     * 同步redis选课数据
     *
     * @param taskId
     * @return
     */
    @Override
    public Boolean syncTeachingClassStudent(Integer taskId) {
        List<TeachingClassStudent> teachingClassStudents = new ArrayList<>();
        Map<Object, Object> teachingClassStudentMap = redisUtil.hmget(getCacheKeys(taskId, null, 3, null));
        for (Object value : teachingClassStudentMap.values()) {
            teachingClassStudents.add((TeachingClassStudent) value);
        }
        if (teachingClassStudents.size() > 0) {
            Consumer<List<TeachingClassStudent>> studentConsumer = o -> teachingClassStudentMapper.addBatchSetTime(o);
            BatchInsert.batchSplitInsert(teachingClassStudents, studentConsumer);
        }

        //同步数据库时，可能有学生选课。所以选择一个一个删除
        List<Object> keys = new ArrayList<>(teachingClassStudentMap.keySet());
        if (keys.size() > 0) {
            redisUtil.hdel(getCacheKeys(taskId, null, 3, null), keys.toArray(new Object[0]));
        }
        return true;
    }

    @Override
    public PageInfo<Student> getStudentsByTeachingClassId(Page<Student> page, Long teachingClassId) {
        IPage<Student> studentByTeachingClassId = studentMapper.getStudentByTeachingClassId(page, Collections.singletonList(teachingClassId));
        return PagingUtil.formatPageResult(studentByTeachingClassId);
    }

    @Override
    public Boolean clearCacheByTeachingClass(Integer taskId, CacheTeachingClassDto cacheTeachingClassDto) {

        List<Long> teachingClassIds = cacheTeachingClassDto.getTeachingClassIds();
        //删除教学班详情
        teachingClassIds.forEach(o -> {
            redisUtil.hdel(getCacheKeys(taskId, null, 6, null), o.toString());
        });
        List<TeachingClass> teachingClassList = teachingClassMapper.selectList(new LambdaQueryWrapper<TeachingClass>()
                .eq(TeachingClass::getTaskId, taskId).in(TeachingClass::getTeachingClassId, teachingClassIds));

        List<Student> students = new ArrayList<>();

        if (CollUtil.isNotEmpty(teachingClassList)) {

            List<ParameterSetting> parameterSettingList = new ArrayList<>();

            teachingClassList.forEach(o -> {
                List<ParameterSetting> list = parameterSettingService.lambdaQuery()
                        .eq(ParameterSetting::getTaskId, taskId)
                        .eq(ParameterSetting::getGradeId, o.getGradeId())
                        .eq(ParameterSetting::getCourseId, o.getCourseId())
                        .eq(ParameterSetting::getFlag, 1)
                        .eq(ParameterSetting::getTeachingClassType, TeachingClassType.SELECT.getCode())
                        .eq(ParameterSetting::getDeleted, Deleted.NOT.getCode())
                        .list();
                parameterSettingList.addAll(list);
            });

            if (CollUtil.isNotEmpty(parameterSettingList)) {
                List<String> collect = parameterSettingList.stream().map(ParameterSetting::getClassIdList).distinct().collect(Collectors.toList());
                if (CollUtil.isNotEmpty(collect)) {

                    List<Long> collect1 = new ArrayList<>();
                    collect.forEach(o -> {
                        List<Long> collect2 = new ArrayList<>(Arrays.asList(o.split(","))).stream().distinct().map(Long::valueOf).collect(Collectors.toList());
                        collect1.addAll(collect2);
                    });
                    if (CollUtil.isNotEmpty(collect1)) {
                        List<ClassInfo> list = classInfoService.lambdaQuery().eq(ClassInfo::getTaskId, taskId).in(ClassInfo::getId, collect1).list();
                        if (CollUtil.isNotEmpty(list)) {
                            List<String> collect2 = list.stream().map(ClassInfo::getClassId).distinct().collect(Collectors.toList());
                            students = studentMapper.selectList(new LambdaQueryWrapper<Student>().in(Student::getClassId, collect2));
                        }
                    }
                }
            }
        }
        List<String> studentTeachingClassKeys = new ArrayList<>();
        students.forEach(student -> {
            studentTeachingClassKeys.add(getCacheKeys(taskId, student.getStudentId(), 4, "Online"));
            studentTeachingClassKeys.add(getCacheKeys(taskId, student.getStudentId(), 4, "Primary"));
        });

        if (studentTeachingClassKeys.size() > 0) {
            redisUtil.del(studentTeachingClassKeys.toArray(new String[0]));
        }
        return true;
    }

    @Override
    public List<TeachingClassStudent> batchAddStudent(BatchSetStudent batchSetStudent) {
        List<TeachingClassStudent> teachingClassStudentList = new ArrayList<>();
        batchSetStudent.getStudentIds().forEach(studentId -> teachingClassStudentList.add(new TeachingClassStudent()
                .setTeachingClassId(batchSetStudent.getTeachingClassId())
                .setStudentId(studentId)
                .setDeleted(Deleted.NOT.getCode())
                .setFlag(batchSetStudent.getFlag())));
        teachingClassStudentMapper.addBatch(teachingClassStudentList);


        List<String> studentTeachingClassKeys = new ArrayList<>();
        String standby = batchSetStudent.getTeachingClassType() == 1 ? "Online" : "Primary";
        batchSetStudent.getStudentIds().forEach(student -> {
            studentTeachingClassKeys.add(getCacheKeys(batchSetStudent.getTaskId(), student, 4, standby));
        });

        if (studentTeachingClassKeys.size() > 0) {
            redisUtil.del(studentTeachingClassKeys.toArray(new String[0]));
        }

        //删除教学班详情 主要只是为了更新人数
        redisUtil.hdel(getCacheKeys(batchSetStudent.getTaskId(), null, 6, null), batchSetStudent.getTeachingClassId().toString());
        return teachingClassStudentList;
    }

    @Override
    public int cancelCourseOptimize(AddCancelCourse addCancelCourse) {
        try {
            //构建键值列表
            String teachingClassDetailKey = getCacheKeys(addCancelCourse.getTaskId(), null, 6, null);
            String studentOptionalClassKey = getCacheKeys(addCancelCourse.getTaskId(), addCancelCourse.getStudentId(), 4, addCancelCourse.getTeachingClassType() == 1 ? "Online" : "Primary");
            String teachingClassStudentKey = getCacheKeys(addCancelCourse.getTaskId(), null, 3, null);

            List<String> keys = ListUtil.toLinkedList(teachingClassDetailKey, studentOptionalClassKey, teachingClassStudentKey);
            Long execute = redisTemplate.execute(cancelCourseLua, keys, addCancelCourse.getStudentId(), addCancelCourse.getTeachingClassId(),
                    LocalDateTime.now(), addCancelCourse.getFlag());

            //查找选课教学班
            TeachingClass teachingClass = teachingClassMapper.selectOne(new LambdaQueryWrapper<TeachingClass>()
                    .eq(TeachingClass::getTaskId, addCancelCourse.getTaskId())
                    .eq(TeachingClass::getTeachingClassId, addCancelCourse.getTeachingClassId())
                    .eq(TeachingClass::getDeleted, Deleted.NOT.getCode()));

            if (ObjUtil.isNotNull(teachingClass)) {
                //查找选课课程
                Course course = courseMapper.selectOne(new LambdaQueryWrapper<Course>()
                        .eq(Course::getTaskId, addCancelCourse.getTaskId()).eq(Course::getCourseId, teachingClass.getCourseId())
                        .eq(Course::getDeleted, Deleted.NOT.getCode()));
                //查找是否为重修学生
                Student student = studentMapper.selectOne(new LambdaQueryWrapper<Student>().eq(Student::getTaskId, addCancelCourse.getTaskId())
                        .eq(Student::getStudentId, addCancelCourse.getStudentId()).eq(Student::getDeleted, Deleted.NOT.getCode()));
                if (ObjUtil.isNotNull(course) && ObjUtil.isNotNull(student)) {
                    //修改重修学生信息
                    StudentRetake studentRetake = studentRetakeMapper.selectOne(new LambdaQueryWrapper<StudentRetake>()
                            .eq(StudentRetake::getTaskId, addCancelCourse.getTaskId())
                            .eq(StudentRetake::getBaseStudentId, student.getBaseStudentId())
                            .eq(StudentRetake::getBaseCourseId, course.getBaseCourseId())
                            .eq(StudentRetake::getDeleted, Deleted.NOT.getCode()));

                    if (ObjUtil.isNotNull(studentRetake)) {
                        studentRetakeMapper.update(studentRetake, new LambdaUpdateWrapper<StudentRetake>().set(StudentRetake::getIsAllocation, 0));
                    }
                }
            }
            return execute.intValue();
        } catch (RedisConnectionFailureException e) {
            return -2;
        } catch (Exception e) {
            return -3;
        }
    }

    @Override
    public PageInfo<StudentCourseVo> getDeselectStudent(Integer taskId, IPage page, String majorId, String gradeId, String classId, String keyWord) {
        CssTaskInfo cssTaskInfo = cssTaskInfoService.getCssTaskInfo(taskId);
        CssTaskRound cssTaskRound = cssTaskRoundService.getOne(new LambdaQueryWrapper<CssTaskRound>()
                .eq(CssTaskRound::getCssTaskId, cssTaskInfo.getCssTaskId())
                .eq(CssTaskRound::getSelectRound, 3).eq(CssTaskRound::getDeleted, Deleted.NOT.getCode()));
        if (ObjUtil.isNull(cssTaskRound)) {
            throw new BizException(5, "请先设置退选学生选课时段");
        }
        IPage<StudentCourseVo> selectStudent = studentMapper.getSelectStudent(taskId, page, majorId, gradeId, classId, keyWord, 1, 4, cssTaskRound.getBeginTime());
        return PagingUtil.formatPageResult(selectStudent);

    }

    @Override
    public PageInfo<Student> getStudentByConditions(Integer taskId, IPage page, String gradeId, String classId, String keyWord) {
        IPage<Student> studentByConditions = studentMapper.getStudentByConditions(taskId, page, gradeId, classId, keyWord);
        return PagingUtil.formatPageResult(studentByConditions);
    }

    @Override
    public int cancelCourse(Integer taskId, Long studentId, Long teachingClassId, Integer flag) {
        return 0;
    }

    @Override
    public List<StudentTeachingClass> getSelectedByStudentId(Integer taskId, Long studentId, Integer teachingClassType) {
        Student student = this.getById(studentId);
        //已选教学班 包括主修 辅修
        //获取全部可选教学班
        HashMap<Long, RedisStudentOptionalClass> redisStudentOptionalClassHashMap = this.getAvailableTeachingClass(taskId, teachingClassType, student);
        //过滤已选
        List<RedisStudentOptionalClass> selectedClass = redisStudentOptionalClassHashMap.values().stream().filter(a -> ObjUtil.isNotNull(a.getFlag()) && a.getFlag() == 1).collect(Collectors.toList());
        List<StudentTeachingClass> mergedList = this.getTeachingClassDetail(taskId, selectedClass.stream().map(RedisStudentOptionalClass::getTeachingClassId).collect(Collectors.toList()));

        //赋值学生属性
        mergedList.forEach(teachingClass -> {
            RedisStudentOptionalClass redisStudentOptionalClass = redisStudentOptionalClassHashMap.get(teachingClass.getTeachingClassId());
            teachingClass.setFlag(redisStudentOptionalClass.getFlag());
            teachingClass.setCourseFlag(redisStudentOptionalClass.getCourseFlag());
        });

        //环节教学班 非在线选课教学班
//        mergedList.addAll(getNormalTeachingClass(taskId, studentId));
        return mergedList;
    }

    @Override
    public List<StudentByClass> getStudentListByTeachingClassId(Integer taskId, Long teachingClassId) {


        List<StudentByClass> studentByClasses = new ArrayList<>();


        List<Student> students1 = studentMapper.selectList(new LambdaQueryWrapper<Student>().eq(Student::getTaskId, taskId).eq(Student::getDeleted, Deleted.NOT.getCode()));


        List<TeachingClass> teachingClassList = teachingClassMapper.selectList(new LambdaQueryWrapper<TeachingClass>()
                .eq(TeachingClass::getTaskId, taskId).eq(TeachingClass::getTeachingClassId, teachingClassId)
                .eq(TeachingClass::getDeleted, Deleted.NOT.getCode()));

        List<Student> students = new ArrayList<>();

        if (CollUtil.isNotEmpty(teachingClassList)) {
            TeachingClass teachingClass = teachingClassList.get(0);

            List<ClassInfo> classInfoList = classInfoService.lambdaQuery().eq(ClassInfo::getTaskId, taskId)
                    .eq(ClassInfo::getGradeId, teachingClass.getGradeId()).list();

            if (CollUtil.isNotEmpty(classInfoList)) {

                List<String> classIds = classInfoList.stream().map(ClassInfo::getClassId).distinct().collect(Collectors.toList());

                List<Student> studentList = students1.stream().filter(p -> classIds.contains(p.getClassId())).distinct().collect(Collectors.toList());


                if (teachingClass.getFlag().equals(1)) {
                    List<Course> courseList = courseMapper.selectList(new LambdaQueryWrapper<Course>()
                            .eq(Course::getTaskId, taskId)
                            .eq(Course::getCourseId, teachingClass.getCourseId())
                            .eq(Course::getDeleted, Deleted.NOT.getCode()));

                    if (CollUtil.isNotEmpty(courseList)) {
                        Course course = courseList.get(0);

                        List<PlanTeachingCourseSegment> planTeachingCourseSegmentList = planTeachingCourseSegmentMapper
                                .selectList(new LambdaQueryWrapper<PlanTeachingCourseSegment>().eq(PlanTeachingCourseSegment::getTaskId, taskId)
                                        .eq(PlanTeachingCourseSegment::getBaseCourseId, course.getBaseCourseId())
                                        .eq(PlanTeachingCourseSegment::getDeleted, Deleted.NOT.getCode()));


                        List<String> teachingPlanId = planTeachingCourseSegmentList.stream()
                                .map(PlanTeachingCourseSegment::getBaseTeachingPlanId)
                                .distinct().collect(Collectors.toList());


                        List<PlanTeaching> planTeachingList = planTeachingMapper.selectList(new LambdaQueryWrapper<PlanTeaching>()
                                .eq(PlanTeaching::getTaskId, taskId).in(PlanTeaching::getBaseTeachingPlanId, teachingPlanId).eq(PlanTeaching::getDeleted, Deleted.NOT.getCode()));


                        List<PlanTeaching> planTeachings = planTeachingList.stream()
                                .filter(p -> p.getGradeId().equals(teachingClass.getGradeId()))
                                .collect(Collectors.toList());


                        List<String> planTeachingIds = planTeachings.stream().map(PlanTeaching::getBaseTeachingPlanId).distinct().collect(Collectors.toList());

                        List<PlanTeachingStudent> planTeachingStudentList = planTeachingStudentMapper.selectList(new LambdaQueryWrapper<PlanTeachingStudent>()
                                .eq(PlanTeachingStudent::getTaskId, taskId).in(PlanTeachingStudent::getBaseTeachingPlanId, planTeachingIds)
                                .eq(PlanTeachingStudent::getDeleted, Deleted.NOT.getCode()));


                        students = studentList.stream().filter(p -> planTeachingStudentList.stream().anyMatch(o -> o.getBaseStudentId().equals(p.getBaseStudentId())))
                                .distinct().collect(Collectors.toList());


                        List<CssTaskInfo> cssTaskInfos = cssTaskInfoService.lambdaQuery().eq(CssTaskInfo::getTaskId, taskId)
                                .eq(CssTaskInfo::getDeleted, Deleted.NOT.getCode()).list();


                        if (CollUtil.isNotEmpty(cssTaskInfos)) {
                            CssTaskInfo cssTaskInfo = cssTaskInfos.get(0);
                            if (cssTaskInfo.getIsRetake().equals(1)) {

                                TaskInfo taskInfo = taskInfoService.getById(taskId);
                                List<StudentRetake> studentRetakeList = studentRetakeMapper.selectList(new LambdaQueryWrapper<StudentRetake>()
                                        .eq(StudentRetake::getTaskId, taskId).eq(StudentRetake::getFlag, 1)
                                        .eq(StudentRetake::getSchoolId, taskInfo.getSchoolId())
                                        .eq(StudentRetake::getTerm, taskInfo.getTerm())
                                        .eq(StudentRetake::getBaseCourseId, course.getBaseCourseId())
                                        .eq(StudentRetake::getIsAllocation, 0)
                                        .eq(StudentRetake::getDeleted, Deleted.NOT.getCode()));

                                List<String> baseStudent = studentRetakeList.stream().map(StudentRetake::getBaseStudentId).distinct().collect(Collectors.toList());


                                List<Student> studentList1 = students1.stream().filter(p -> baseStudent.contains(p.getBaseStudentId())).distinct().collect(Collectors.toList());


                                students.addAll(studentList1);

                            }
                        }
                    }
                }
            }

            //去重
            List<TeachingClass> teachingClasses = teachingClassMapper.selectList(new LambdaQueryWrapper<TeachingClass>().eq(TeachingClass::getTaskId, taskId)
                    .eq(TeachingClass::getGradeId, teachingClass.getGradeId())
                    .eq(TeachingClass::getCourseId, teachingClass.getCourseId())
                    .eq(TeachingClass::getTeachingClassType, TeachingClassType.SELECT.getCode())
                    .eq(TeachingClass::getDeleted, Deleted.NOT.getCode()));

            List<Long> teachingClassIds = teachingClasses.stream().map(TeachingClass::getTeachingClassId).distinct().collect(Collectors.toList());


            List<TeachingClassStudent> teachingClassStudents = teachingClassStudentMapper.getStudentList(teachingClassIds);

            List<Student> afterSelectStudents = students.stream().filter(p -> teachingClassStudents.stream().anyMatch(o -> o.getStudentId().equals(p.getStudentId()))).distinct()
                    .collect(Collectors.toList());

            students.removeAll(afterSelectStudents);

        }


        Map<String, List<Student>> studentMap = students.stream().distinct().collect(Collectors.groupingBy(Student::getClassId));
        studentMap.forEach((key, value) -> {
            StudentByClass studentByClass = new StudentByClass();

            studentByClass.setClassId(key);
            if (CollUtil.isNotEmpty(value)) {
                Student student = value.get(0);
                studentByClass.setClassName(student.getClassName());
            }
            studentByClass.setStudentList(value);
            studentByClasses.add(studentByClass);
        });
        return studentByClasses;
    }

    @Override
    public void deselectStudentToExcel(HttpServletResponse response, Integer taskId, String majorId, String majorName, String gradeId, String gradeName, String classId, String className, String keyWord, Integer currentPage, Integer pageSize) {

        TaskInfo taskInfo = taskInfoService.getById(taskId);

        String title = "{}退选信息导出  学期：{}   年级：{}  班级：{}";
        String format = StrUtil.format(title, taskInfo.getSchoolName(), taskInfo.getTermName()
                , StringUtils.isNotBlank(gradeName) ? gradeName : "全部年级",
                StringUtils.isNotBlank(className) ? className : "全部班级");

        List<DeselectStudentToExcel> deselectStudentToExcelList = new ArrayList<>();

        Page<StudentCourseVo> page = new Page<>(currentPage, pageSize);

        PageInfo<StudentCourseVo> deselectStudent = this.getDeselectStudent(taskId, page, majorId, gradeId, classId, keyWord);
        List<StudentCourseVo> list = deselectStudent.getList();

        list.forEach(o -> {
            DeselectStudentToExcel deselectStudentToExcel = new DeselectStudentToExcel();
            deselectStudentToExcel.setStudentName(o.getStudentName());
            deselectStudentToExcel.setMajorName(o.getMajorName());
            deselectStudentToExcel.setGradeName(o.getGradeName());
            deselectStudentToExcel.setTeachingClassName(o.getTeachingClassName());
            deselectStudentToExcel.setCourseName(o.getCourseName());
            deselectStudentToExcel.setDeselectTime(o.getModifiedTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            deselectStudentToExcelList.add(deselectStudentToExcel);
        });

        String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy_MM_dd_HH_mm_ss"));


        response.setHeader("content-Type", "application/vnd.ms-excel");

        MyExcelUtils.exportExcel(deselectStudentToExcelList,
                format, "",
                DeselectStudentToExcel.class,
                "退选信息已导出" + time + ".xls", response);


    }

    @Override
    public List<BaseClassInfoDto> getClassDto(Integer taskId) {
        List<ClassInfo> list = classInfoService.lambdaQuery().eq(ClassInfo::getTaskId, taskId).list();

        List<BaseClassInfoDto> classInfoDtoList = new ArrayList<>();

        list.forEach(o -> {
            BaseClassInfoDto classInfoDto = new BaseClassInfoDto();
            classInfoDto.setClassId(o.getClassId());
            classInfoDto.setClassName(o.getClassName());
            classInfoDtoList.add(classInfoDto);
        });

        return classInfoDtoList.stream().distinct().collect(Collectors.toList());


    }

    @Override
    public List<CourseTypeDto> getCourseTypeByTaskId(Integer taskId, Long studentId, String baseStudentId) {


        List<CourseTypeDto> courseTypeDtoList = new ArrayList<>();

        HashMap<Long, RedisStudentOptionalClass> redisStudentOptionalClassHashMap = this.getAvailableTeachingClass(taskId, 1, studentId, baseStudentId);
        List<StudentTeachingClass> availableTeachingClass = this.getTeachingClassDetail(taskId, new ArrayList<>(redisStudentOptionalClassHashMap.keySet()));
        Map<String, String> collect = availableTeachingClass.stream().collect(Collectors.toMap(StudentTeachingClass::getCourseTypeId, StudentTeachingClass::getCourseType, (o1, o2) -> o1));

        collect.forEach((key, value) -> {
            CourseTypeDto courseTypeDto = new CourseTypeDto();
            courseTypeDto.setTypeId(key);
            courseTypeDto.setTypeName(value);
            courseTypeDtoList.add(courseTypeDto);

        });

        return courseTypeDtoList;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updateIsRetake(Integer taskId) {
        List<StudentRetake> retakes = studentRetakeService.lambdaQuery().eq(StudentRetake::getTaskId, taskId).list();

        List<String> baseStudentIds = retakes.stream().map(StudentRetake::getBaseStudentId).distinct().collect(Collectors.toList());

        List<StudentRetake> ret = new ArrayList<>();

        Map<String, List<StudentRetake>> retakeStudent = retakes.stream().collect(Collectors.groupingBy(StudentRetake::getBaseStudentId));
        if (CollUtil.isNotEmpty(baseStudentIds)) {

            List<Student> studentList = studentMapper.selectList(new LambdaQueryWrapper<Student>().in(Student::getBaseStudentId, baseStudentIds).eq(Student::getTaskId, taskId));

            List<Long> studentIds = studentList.stream().map(Student::getStudentId).collect(Collectors.toList());

            if (CollUtil.isNotEmpty(studentIds)) {

                List<TeachingClassStudentDto> teachingClassStudentDtos = studentMapper.selectTeachingClassStudentDto(taskId, studentIds);

                Map<String, List<TeachingClassStudentDto>> courseMap = teachingClassStudentDtos.stream().collect(Collectors.groupingBy(TeachingClassStudentDto::getBaseStudentId));

                courseMap.forEach((key, value) -> {
                    //需要更新的重修课程
                    List<StudentRetake> studentRetakeList = retakeStudent.get(key);
                    //学生已选的课程
                    List<String> baseCourseIds = value.stream().map(TeachingClassStudentDto::getBaseCourseId).distinct().collect(Collectors.toList());

                    //已经分配教学班的学生
                    List<StudentRetake> studentRetakes = studentRetakeList.stream().filter(p -> baseCourseIds.contains(p.getBaseCourseId())).collect(Collectors.toList());
                    studentRetakes.forEach(o -> {
                        o.setIsAllocation(1);
                        ret.add(o);
                    });

                    if (CollUtil.isNotEmpty(studentRetakes)) {
                        studentRetakeList.removeAll(studentRetakes);
                    }
                    studentRetakeList.forEach(o -> {
                        o.setIsAllocation(0);
                        ret.add(o);
                    });
                });
            }
            studentRetakeService.saveOrUpdateBatch(ret);
        }


        return "重修学生更新成功";
    }

    public HashMap<Long, RedisStudentOptionalClass> getAvailableTeachingClass(Integer taskId, Integer teachingClassType, Student student) {
        HashMap<Long, RedisStudentOptionalClass> redisStudentOptionalClassSet = new HashMap();
        if (redisUtil.hasKey(getCacheKeys(taskId, student.getStudentId(), 4, teachingClassType == 1 ? "Online" : "Primary"))) {
            redisUtil.hmget(getCacheKeys(taskId, student.getStudentId(), 4, teachingClassType == 1 ? "Online" : "Primary")).forEach((k, v) -> {
                redisStudentOptionalClassSet.put(Long.parseLong(k.toString()), (RedisStudentOptionalClass) v);
            });
        } else {
            List<TeachingClassCourseDto> availableCourse = this.getAvailableCourse(taskId, student.getBaseStudentId(), student.getSelectionScope(), null, null, null, null);
            //非重修教学班
            List<Long> courseIds = availableCourse.stream().filter(a -> a.getState().equals(1)).map(TeachingClassCourseDto::getCourseId).collect(Collectors.toList());


            if (CollUtil.isNotEmpty(courseIds)) {
                studentMapper.getTeachingClass(teachingClassType, courseIds, null, student.getStudentId(), student.getGradeId())
                        .forEach(a -> {
                            redisStudentOptionalClassSet.put(a.getTeachingClassId(), RedisStudentOptionalClass.builder()
                                    .teachingClassId(a.getTeachingClassId())
                                    .courseId(a.getCourseId())
                                    .flag(a.getFlag())
                                    .courseFlag(1).build());
                        });
            }

            //重修课程教学班
            List<Long> courseRetakeIds = availableCourse.stream().filter(a -> a.getState().equals(2)).map(TeachingClassCourseDto::getCourseId).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(courseRetakeIds)) {
                studentMapper.getRetakeTeachingClass(teachingClassType, courseRetakeIds, null, student.getStudentId())
                        .forEach(a -> {
                            redisStudentOptionalClassSet.put(a.getTeachingClassId(), RedisStudentOptionalClass.builder()
                                    .teachingClassId(a.getTeachingClassId())
                                    .courseId(a.getCourseId())
                                    .flag(a.getFlag())
                                    .courseFlag(2).build());
                        });
            }
            //转化格式
            Map<String, Object> resultMap = new HashMap<>();
            for (Map.Entry<Long, RedisStudentOptionalClass> optionalClassEntry : redisStudentOptionalClassSet.entrySet()) {
                resultMap.put(optionalClassEntry.getKey().toString(), optionalClassEntry.getValue());

            }

            redisUtil.hmset2(getCacheKeys(taskId, student.getStudentId(), 4, teachingClassType == 1 ? "Online" : "Primary"), resultMap, 60 * 60 * 24 * 7);
        }
        return redisStudentOptionalClassSet;
    }

    /**
     * 获取在线可选教学班
     *
     * @param condition
     * @return
     */
    private StudentTeachingClassView getAvailableTeachingClassOnline(GetAvailableCondition condition) {
        StudentTeachingClassView studentTeachingClassView = new StudentTeachingClassView();
        //获取全部可选教学班 放到最前面，先计算已选教学班，选课要求
        HashMap<Long, RedisStudentOptionalClass> redisStudentOptionalClassHashMap = this.getAvailableTeachingClass(condition.getTaskId(), condition.getTeachingClassType(), condition.getStudentId(), condition.getBaseStudentId());
        List<StudentTeachingClass> availableTeachingClass = this.getTeachingClassDetail(condition.getTaskId(), new ArrayList<>(redisStudentOptionalClassHashMap.keySet()));
        this.getTeachingClassTimeCode(condition.getTaskId());
        //赋值学生属性
        availableTeachingClass.forEach(teachingClass -> {
            RedisStudentOptionalClass redisStudentOptionalClass = redisStudentOptionalClassHashMap.get(teachingClass.getTeachingClassId());
            teachingClass.setFlag(redisStudentOptionalClass.getFlag());
            teachingClass.setCourseFlag(redisStudentOptionalClass.getCourseFlag());
        });
        //过滤已选
        List<StudentTeachingClass> selectedClass = availableTeachingClass.stream()
                .filter(a -> ObjUtil.isNotNull(a.getFlag()) && a.getFlag() == 1).collect(Collectors.toList());


        if (StringUtils.isNotBlank(condition.getKeyWord())) {
            selectedClass = selectedClass.stream().filter(p -> p.getCourseName().contains(condition.getKeyWord())).collect(Collectors.toList());
        }
        studentTeachingClassView.setSelectedTeachingClassList(selectedClass);
        //当前选课数量
        studentTeachingClassView.setCurrentCourse(selectedClass.stream().map(StudentTeachingClass::getCourseId).distinct().count());
        //当前总学分
        studentTeachingClassView.setCurrentTotalPoint(selectedClass.stream().mapToDouble(StudentTeachingClass::getCredit).sum());

        //正选补选需要过滤轮次设置的条件，退选需要过滤已选教学班
        if (condition.getSelectRound().equals(3)) {
            availableTeachingClass = availableTeachingClass.stream().filter(a -> ObjUtil.isNotNull(a.getFlag()) && a.getFlag() == 1).collect(Collectors.toList());
        } else {
            List<CssRange> cssRanges = condition.getCssRanges();
            List<String> grades = new ArrayList<>();
            if (CollUtil.isNotEmpty(cssRanges)) {
                grades = cssRanges.stream().map(CssRange::getGradeName).distinct().collect(Collectors.toList());
            }
            if (CollUtil.isNotEmpty(grades)) {
                List<String> finalGrades = grades;
                availableTeachingClass = availableTeachingClass.stream()
                        //根据课程范围获取 可选课程
                        .filter(teachingClass -> finalGrades.contains(teachingClass.getGradeName()))
                        .collect(Collectors.toList());
            }
        }

        //判断是否显示重修教学班.只有补选 退选才显示重修教学班 //要不然只显示非重修的教学班
        if (!condition.getSelectRound().equals(1)) {
            if (condition.getIsRetake().equals(1)) {

            } else {
                availableTeachingClass = availableTeachingClass.stream()
                        .filter(teachingClass -> teachingClass.getCourseFlag().equals(1))
                        .collect(Collectors.toList());
            }
        } else {
            availableTeachingClass = availableTeachingClass.stream()
                    .filter(teachingClass -> teachingClass.getCourseFlag().equals(1))
                    .collect(Collectors.toList());
        }

        availableTeachingClass = availableTeachingClass.stream()
                //根据筛选条件筛选可选教学班
                .filter(teachingClass ->
                        (StrUtil.isBlank(condition.getCourseTypeId()) || condition.getCourseTypeId().equals(teachingClass.getCourseTypeId())) &&
                                (ObjUtil.isNull(condition.getCourseNature()) || condition.getCourseNature().equals(0) || condition.getCourseNature().equals(teachingClass.getCourseNature())) &&
                                ((ObjUtil.isNull(condition.getCredit()) || (condition.getCredit().equals(4.0) && teachingClass.getCredit() > 3)) || teachingClass.getCredit().equals(condition.getCredit())) &&
                                (StrUtil.isBlank(condition.getKeyWord()) || teachingClass.getCourseName().contains(condition.getKeyWord())) &&
                                (ObjUtil.isNull(condition.getStudyNature()) || condition.getStudyNature().equals(teachingClass.getStudyNature()))
                )
                //筛选有容量的教学班
                .filter(teachingClass -> !condition.getIsExist().equals(1) || teachingClass.getStudentNumber() < teachingClass.getClassNum())
                .collect(Collectors.toList());

        Map<Long, List<StudentTeachingClass>> studentTeachingMap = availableTeachingClass.stream().collect(Collectors.groupingBy(StudentTeachingClass::getCourseId));
        List<StudentClassDto> studentClassDtoList = new ArrayList<>();

        for (Map.Entry<Long, List<StudentTeachingClass>> entry : studentTeachingMap.entrySet()) {
            StudentClassDto studentClassDto = new StudentClassDto();
            studentClassDto.setCourseId(entry.getKey());
            int flag = 0;
            for (StudentTeachingClass studentTeachingClass : entry.getValue()) {
                studentClassDto.setCourseName(studentTeachingClass.getCourseName());
                studentClassDto.setCourseCode(studentTeachingClass.getCourseCode());

                if (studentTeachingClass.getFlag() != null) {
                    if (studentTeachingClass.getFlag().equals(1)) {
                        flag = 1;
                    }
                }

            }
            studentClassDto.setFlag(flag);
            studentClassDto.setStudentTeachingClassList(entry.getValue());
            studentClassDtoList.add(studentClassDto);
        }


        int size = studentClassDtoList.stream().map(StudentClassDto::getCourseId).distinct().collect(Collectors.toList()).size();

        studentTeachingClassView.setCourseNum((long) size);

        studentTeachingClassView.setPageInfo(PagingUtil.getPages(condition.getCurrentPage(), condition.getPageSize(), studentClassDtoList));

        return studentTeachingClassView;

    }

    private void getTeachingClassTimeCode(Integer taskId) {
        //存储timecode todo
        if (redisUtil.hasKey(getCacheKeys(taskId, null, 7, null))) {
            return;
        } else {
            List<Integer> taskIds = new ArrayList<>();
            taskIds.add(taskId);
            List<ViewSchedule> allSchedule = scheduleMapper.getAllSchedule(taskIds);
            Map<Long, List<ViewSchedule>> collect = allSchedule.stream().collect(Collectors.groupingBy(ViewSchedule::getTeachingClassId));
            collect.forEach((key, value) -> {
                List<TeachingClassTimeCode> teachingClassTimeCodes = new ArrayList<>();
                for (ViewSchedule viewSchedule : value) {
                    TeachingClassTimeCode teachingClassTimeCode = new TeachingClassTimeCode();
                    teachingClassTimeCode.setCourseId(viewSchedule.getCourseId());
                    teachingClassTimeCode.setTimeCode(viewSchedule.getTimeCode());
                    teachingClassTimeCodes.add(teachingClassTimeCode);
                }
                Map<String, List<TeachingClassTimeCode>> map = new HashMap<>();
                map.put(String.valueOf(key), teachingClassTimeCodes);
                redisUtil.hmset4(getCacheKeys(taskId, null, 7, null), map, 60 * 60 * 24);
            });
        }
    }

    public List<StudentTeachingClass> getTeachingClassDetail(Integer taskId, List<Long> teachingClassIds) {
        List<StudentTeachingClass> list = new ArrayList<>();
        if (CollUtil.isEmpty(teachingClassIds)) {
            return list;
        }
        redisUtil.hgets(getCacheKeys(taskId, null, 6, null), teachingClassIds.stream().map(String::valueOf).collect(Collectors.toSet()))
                .forEach(a -> {
                    if (a != null) {
                        list.add((StudentTeachingClass) a);
                    }
                });
        //redis 不存在的数据则查询数据库并缓存
        teachingClassIds.removeAll(list.stream().map(StudentTeachingClass::getTeachingClassId).collect(Collectors.toList()));
        if (CollUtil.isNotEmpty(teachingClassIds)) {
            List<StudentTeachingClass> courseTeachingClass = studentMapper.getNormalCourseTeachingClass(teachingClassIds, null);
            courseTeachingClass = courseTeachingClass.stream().distinct().collect(Collectors.toList());
            //填充教师信息
            Map<Long, List<Teacher>> teacherMap = teacherService.getTeacherByTeachingClassIds(teachingClassIds).stream()
                    .collect(Collectors.groupingBy(Teacher::getTeachingClassId));
            //课表信息
            List<ScheduleDto> scheduleDtoList = scheduleMapper.getAllScheduleByTeachingClassIds(null, teachingClassIds);
            Map<Long, List<ScheduleDto>> scheduleMap = scheduleDtoList.stream().collect(Collectors.groupingBy(ScheduleDto::getTeachingClassId));
            //学生人数
            Map<Long, Integer> studentNumMap = teachingClassStudentMapper.getStudentNumber(teachingClassIds)
                    .stream().collect(Collectors.toMap(TeachingClassCourseDto::getTeachingClassId, TeachingClassCourseDto::getStudentNumber));
            courseTeachingClass.forEach(a -> {

                List<Teacher> teacherList = teacherMap.get(a.getTeachingClassId());
                List<Teacher> teacherList1 = new ArrayList<>();
                Set<String> baseTeacherId = new HashSet<>();
                if (CollUtil.isNotEmpty(teacherList)) {
                    teacherList.forEach(o -> {
                        if (!baseTeacherId.contains(o.getBaseTeacherId())) {
                            baseTeacherId.add(o.getBaseTeacherId());
                            teacherList1.add(o);
                        }
                    });
                }
                if (CollUtil.isNotEmpty(teacherList1)) {
                    a.setTeachers(teacherList1);
                }
                a.setSchedules(scheduleMap.get(a.getTeachingClassId()));
                //学生人数
                a.setStudentNumber(studentNumMap.getOrDefault(a.getTeachingClassId(), 0));
            });
            list.addAll(courseTeachingClass);
            //缓存数据
            if (CollUtil.isNotEmpty(courseTeachingClass)) {
                redisUtil.hmset(getCacheKeys(taskId, null, 6, null), courseTeachingClass.stream().collect(Collectors.toMap(a -> a.getTeachingClassId(), a -> a)), 60 * 60 * 24 * 7);
            }
        }
        list.sort(Comparator.comparing(StudentTeachingClass::getTeachingClassId));
        return list;
    }

    private HashMap<Long, RedisStudentOptionalClass> getAvailableTeachingClass(Integer taskId, Integer teachingClassType, Long studentId, String baseStudentId) {

        //获取学生可选教学班ids 包含主修辅修
        HashMap<Long, RedisStudentOptionalClass> redisStudentOptionalClassSet = new HashMap();
        if (redisUtil.hasKey(getCacheKeys(taskId, studentId, 4, teachingClassType == 1 ? "Online" : "Primary"))) {
            redisUtil.hmget(getCacheKeys(taskId, studentId, 4, teachingClassType == 1 ? "Online" : "Primary")).forEach((k, v) -> {
                redisStudentOptionalClassSet.put(Long.parseLong(k.toString()), (RedisStudentOptionalClass) v);
            });
        } else {
            //获取选课课程
            List<TeachingClassCourseDto> availableCourse = this.getAvailableCourse(taskId, baseStudentId, null, null, null, null, null);

            Student student = studentMapper.selectById(studentId);

            //非重修教学班
            List<Long> courseIds = availableCourse.stream().filter(a -> a.getState().equals(1)).map(TeachingClassCourseDto::getCourseId).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(courseIds)) {
                studentMapper.getTeachingClass(teachingClassType, courseIds, null, studentId, student.getGradeId())
                        .forEach(a -> {
                            redisStudentOptionalClassSet.put(a.getTeachingClassId(), RedisStudentOptionalClass.builder()
                                    .teachingClassId(a.getTeachingClassId())
                                    .courseId(a.getCourseId())
                                    .flag(a.getFlag())
                                    .courseFlag(1).build());
                        });
            }

            //重修课程教学班
            List<Long> courseRetakeIds = availableCourse.stream().filter(a -> a.getState().equals(2)).map(TeachingClassCourseDto::getCourseId).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(courseRetakeIds)) {
                studentMapper.getRetakeTeachingClass(teachingClassType, courseRetakeIds, null, studentId)
                        .forEach(a -> {
                            redisStudentOptionalClassSet.put(a.getTeachingClassId(), RedisStudentOptionalClass.builder()
                                    .teachingClassId(a.getTeachingClassId())
                                    .courseId(a.getCourseId())
                                    .flag(a.getFlag())
                                    .courseFlag(2).build());
                        });
            }

            //转化格式
            Map<String, Object> resultMap = new HashMap<>();
            for (Map.Entry<Long, RedisStudentOptionalClass> optionalClassEntry : redisStudentOptionalClassSet.entrySet()) {
                resultMap.put(optionalClassEntry.getKey().toString(), optionalClassEntry.getValue());
            }

            redisUtil.hmset2(getCacheKeys(taskId, studentId, 4, teachingClassType == 1 ? "Online" : "Primary"), resultMap, 60 * 60 * 24 * 7);

        }

        return redisStudentOptionalClassSet;
    }

    /**
     * 获取学生对应的课程
     *
     * @param taskId
     * @param baseStudentId
     * @param studyNature
     * @param courseTypeId
     * @param courseNature
     * @param credit
     * @param keyWord
     * @return
     */
    private List<TeachingClassCourseDto> getAvailableCourse(Integer taskId, String baseStudentId, Integer studyNature, String courseTypeId, Integer courseNature, Double credit, String keyWord) {

        List<TeachingClassCourseDto> courseByPlan = new ArrayList<>();

        /**
         * 获取学生对应教学计划
         */
        PlanTeaching teachingPlanByStu = planTeachingStudentMapper.getTeachingPlanByStu(taskId, baseStudentId, studyNature);
        /**
         *学生获取教学计划 本专业本年级 对应所有课程的教学班
         */
        if (teachingPlanByStu != null) {
            courseByPlan = planTeachingMapper.getCourseByPlan(taskId, courseTypeId, courseNature, credit, keyWord, teachingPlanByStu.getBaseMajorId(), teachingPlanByStu.getGradeId());
        }
        //获取学生重修的课程
        TaskInfo taskInfo = taskInfoService.getById(taskId);
        List<TeachingClassCourseDto> courseRetake = planTeachingMapper.getCourseRetake(taskId, taskInfo.getSchoolId(), taskInfo.getTerm(), courseTypeId, courseNature, credit, keyWord, baseStudentId, studyNature);

        for (TeachingClassCourseDto o : courseRetake) {
            TeachingClassCourseDto teachingClassCourseDto = courseByPlan.stream().filter(p -> p.getCourseId().equals(o.getCourseId())).findFirst().orElse(null);
            if (teachingClassCourseDto != null) {
                courseByPlan.remove(teachingClassCourseDto);
            }
        }

        return Stream.concat(courseByPlan.stream(), courseRetake.stream()).distinct().collect(Collectors.toList());
    }

    private String getCacheKeys(Integer taskId, Long uniqueIdentify, int type, String standby) {
         /*
        返回值用StringBuilder 速度快
        1:存储教学班都有哪些学生  key->teachingClassId
        2:存储学生都选了哪些教学班，教学班详情  key->studentId
        3:存储教学班对应学生数据，然后同步到数据库
        4:学生可选教学班 key->studentId value -> Set<TeachingClassId>
        5:学生已选教学班
        6：教学班详情 包括课表 余量等
         */
        StringBuilder key = new StringBuilder();
        key.append(GlobalConstant.REDIS_PREFIX).append(taskId).append(StrUtil.C_COLON);
        switch (type) {
            case 1:
                return key.append("TeachingStudentId").append(StrUtil.C_COLON).append(uniqueIdentify).toString();
            case 2:
                return key.append("StudentTeachingClass").append(StrUtil.C_COLON).append(uniqueIdentify).toString();
            case 3:
                return key.append("TeachingClassStudent").toString();
            case 4:
                return key.append("StudentOptionalClass").append(StrUtil.C_COLON).append(uniqueIdentify).append(StrUtil.C_COLON).append(standby).toString();
            case 5:
                return key.append("StudentSelectedClass").append(StrUtil.C_COLON).append(uniqueIdentify).toString();
            case 6:
                return key.append("TeachingClassDetail").toString();
            case 7:
                return key.append("TeachingClassCode").toString();
            default:
                return "";
        }
    }
}